package stone926.mods.more_enchantments.mixins;

import net.minecraft.advancement.criterion.Criteria;
import net.minecraft.block.*;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.FlowableFluid;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.*;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.stat.Stats;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.event.GameEvent;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import stone926.mods.more_enchantments.MoreEnchantmentsMod;

@Mixin(BucketItem.class)
public abstract class BucketItemMixin extends Item {

  @Shadow
  @Final
  private Fluid fluid;

  public BucketItemMixin(Settings settings) {
    super(settings);
  }

  @Shadow
  protected abstract void playEmptyingSound(@Nullable PlayerEntity player, WorldAccess world, BlockPos pos);

  @Shadow
  public abstract TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand);

  @Shadow
  public abstract boolean placeFluid(@Nullable PlayerEntity player, World world, BlockPos pos, @Nullable BlockHitResult hitResult);

  @Shadow
  public abstract void onEmptied(@Nullable PlayerEntity player, World world, ItemStack stack, BlockPos pos);

  public boolean placeFluidIgnoreDimension(@Nullable PlayerEntity player, World world, BlockPos pos, @Nullable BlockHitResult hitResult) {
    if (!(this.fluid instanceof FlowableFluid)) {
      return false;
    } else {
      BlockState blockState = world.getBlockState(pos);
      Block block = blockState.getBlock();
      Material material = blockState.getMaterial();
      boolean bl = blockState.canBucketPlace(this.fluid);
      boolean bl2 = blockState.isAir() || bl || block instanceof FluidFillable && ((FluidFillable) block).canFillWithFluid(world, pos, blockState, this.fluid);
      if (!bl2) {
        return hitResult != null && this.placeFluidIgnoreDimension(player, world, hitResult
          .getBlockPos()
          .offset(hitResult.getSide()), null);
      } else if (block instanceof FluidFillable && this.fluid == Fluids.WATER) {
        ((FluidFillable) block).tryFillWithFluid(world, pos, blockState, ((FlowableFluid) this.fluid).getStill(false));
        this.playEmptyingSound(player, world, pos);
        return true;
      } else {
        if (!world.isClient && bl && !material.isLiquid()) {
          world.breakBlock(pos, true);
        }

        if (!world.setBlockState(pos, this.fluid.getDefaultState().getBlockState(), 11) && !blockState
          .getFluidState()
          .isStill()) {
          return false;
        } else {
          this.playEmptyingSound(player, world, pos);
          return true;
        }
      }
    }
  }

  @Inject(method = "use", at = @At("HEAD"), cancellable = true)
  private void useWithPourEnchantment(World world, PlayerEntity user, Hand hand, CallbackInfoReturnable<TypedActionResult<ItemStack>> cir) {
    ItemStack itemStack = user.getStackInHand(hand);
    int pourLvl = EnchantmentHelper.getLevel(MoreEnchantmentsMod.POUR, itemStack);

    if (pourLvl <= 0) return;

    BlockHitResult blockHitResult = raycast(world, user, this.fluid == Fluids.EMPTY ? RaycastContext.FluidHandling.SOURCE_ONLY : RaycastContext.FluidHandling.NONE);

    if (blockHitResult.getType() == HitResult.Type.MISS) {
      cir.setReturnValue(TypedActionResult.pass(itemStack));
      return;
    } else if (blockHitResult.getType() != HitResult.Type.BLOCK) {
      cir.setReturnValue(TypedActionResult.pass(itemStack));
      return;
    } else {
      BlockPos blockPos = blockHitResult.getBlockPos();
      Direction direction = blockHitResult.getSide();
      BlockPos blockPos2 = blockPos.offset(direction);
      if (world.canPlayerModifyAt(user, blockPos) && user.canPlaceOn(blockPos2, direction, itemStack)) {
        BlockState blockState;
        if (this.fluid == Fluids.EMPTY) {
          blockState = world.getBlockState(blockPos);
          if (blockState.getBlock() instanceof FluidDrainable fluidDrainable) {
            ItemStack itemStack2 = fluidDrainable.tryDrainFluid(world, blockPos, blockState);
            if (!itemStack2.isEmpty()) {

              if (EnchantmentHelper.getLevel(MoreEnchantmentsMod.POUR, itemStack2) <= 0)
                itemStack2.addEnchantment(MoreEnchantmentsMod.POUR, pourLvl);

              user.incrementStat(Stats.USED.getOrCreateStat(this));
              fluidDrainable.getBucketFillSound().ifPresent((sound) -> {
                user.playSound(sound, 1.0F, 1.0F);
              });
              world.emitGameEvent(user, GameEvent.FLUID_PICKUP, blockPos);
              ItemStack itemStack3 = ItemUsage.exchangeStack(itemStack, user, itemStack2);


              if (!world.isClient) {
                Criteria.FILLED_BUCKET.trigger((ServerPlayerEntity) user, itemStack2);
              }
              if (world.getDimension().isUltrawarm()) {
                user.damage(DamageSource.DROWN, 4);
              }
              cir.setReturnValue(TypedActionResult.success(itemStack3, world.isClient()));
              return;
            }
          }

          cir.setReturnValue(TypedActionResult.fail(itemStack));
          return;
        } else {
          blockState = world.getBlockState(blockPos);
          BlockPos blockPos3 = blockState.getBlock() instanceof FluidFillable && this.fluid == Fluids.WATER ? blockPos : blockPos2;
          if (this.placeFluidIgnoreDimension(user, world, blockPos3, blockHitResult)) {
            this.onEmptied(user, world, itemStack, blockPos3);
            if (user instanceof ServerPlayerEntity) {
              Criteria.PLACED_BLOCK.trigger((ServerPlayerEntity) user, blockPos3, itemStack);
            }

            user.incrementStat(Stats.USED.getOrCreateStat(this));
            if (world.getDimension().isUltrawarm()) {
              user.damage(DamageSource.ON_FIRE, 4);
            }
            cir.setReturnValue(TypedActionResult.success(getEmptiedStack(itemStack, user, pourLvl), world.isClient()));
            return;
          } else {
            cir.setReturnValue(TypedActionResult.fail(itemStack));
            return;
          }
        }
      } else {
        cir.setReturnValue(TypedActionResult.fail(itemStack));
        return;
      }
    }
  }

  private ItemStack getEmptiedStack(ItemStack stack, PlayerEntity player, int lvl) {
    ItemStack bucket = new ItemStack(Items.BUCKET);
    bucket.addEnchantment(MoreEnchantmentsMod.POUR, lvl);
    return !player.getAbilities().creativeMode ? bucket : stack;
  }

}
